﻿Imports System.IO
Public Class Level
	Implements IDisposable
	' Physical structure of the level.
	Private _tiles As Tile(,)
	Private _layers As Layer() ' dirubah dari Texture2D
	' The layer which entities are drawn on top of.
	Private Const EntityLayer As Integer = 2

	Private cameraPositionX As Single ' menambahkan variable posisi kamera
	Private cameraPositionY As Single '  menambahkan variable posisi kamera

	' Entities in the level.
	Public ReadOnly Property Character As Character()
		Get
			Return _character
		End Get
	End Property
	Private _character As Character()

	'private List<Gem> gems = new List<Gem>()
	'private List<Enemy> enemies = new List<Enemy>()

	'key location in the level
	Private _start As Vector2
	Private _exit As Point = InvalidPosition
	Private Shared ReadOnly InvalidPosition As Point = New Point(-1, -1)

	'level game state
	Private _random As Random = New Random(354668) ' Arbitrary, but constant seed

	Private _cameraPosition As Single 'menambahkan variable posisi kamera

	Public ReadOnly Property Score As Integer
		Get
			Return _score
		End Get
	End Property
	Private _score As Integer

	Public ReadOnly Property ReachedExit As Boolean
		Get
			Return _reachedExit
		End Get
	End Property
	Private _reachedExit As Boolean

	'level content
	Public ReadOnly Property Content As ContentManager
		Get
			Return _content
		End Get
	End Property
	Private _content As ContentManager

#Region "Loading"

	''' <summary>
	''' Constructs a new level.
	''' </summary>
	''' <param name="serviceProvider">
	''' The service provider that will be used to construct a ContentManager.
	''' </param>
	''' <param name="fileStream">
	''' A stream containing the tile data.
	''' </param>
	Public Sub New(serviceProvider As IServiceProvider, fileStream As Stream, levelIndex As Integer)
		' Create a new content manager to load content used just by this level.
		_content = New ContentManager(serviceProvider, "Content")

		LoadTiles(fileStream)

		' Load background layer textures. For now, all levels must
		' use the same backgrounds and only use the left-most part of them.
		_layers = New Layer(3) {}
		For i As Integer = 0 To 3
			'layers[0] = new Layer(Content, "Backgrounds/Layer0", 0.2f)
			'layers[1] = new Layer(Content, "Backgrounds/Layer1", 0.5f)
			'layers[2] = new Layer(Content, "Backgrounds/Layer2", 0.8f)
		Next

		'load sounds
		'exitReachedSound = Content.Load<SoundEffect>("Sounds/ExitReached")
	End Sub


	''' <summary>
	''' Iterates over every tile in the structure file and loads its
	''' appearance and behavior. This method also validates that the
	''' file is well-formed with a player start point, exit, etc.
	''' </summary>
	''' <param name="fileStream">
	''' A stream containing the tile data.
	''' </param>
	Private Sub LoadTiles(fileStream As Stream)
		'load the level and ensure all of the lines are the same length.
		Dim _width As Integer
		Dim _lines As List(Of String) = New List(Of String)()
		Using Reader As StreamReader = New StreamReader(fileStream)
			Dim line As String = Reader.ReadLine()
			_width = line.Length
			While Not (line Is Nothing)
				_lines.Add(line)
				If (line.Length <> _width) Then
					Throw New Exception(String.Format("The length of line {0} is different from all preceeding lines.", _lines.Count))
					line = Reader.ReadLine()
				End If
			End While
		End Using

		' Allocate the tile grid.
		_tiles = New Tile(_width, _lines.Count) {}

		' Loop over every tile position,
		For y As Integer = 0 To Height
			For x As Integer = 0 To _width
				' to load each tile.
				Dim _tileType As Char = _lines(y)(x)
				_tiles(x, y) = LoadTile(_tileType, x, y)
			Next x
		Next y

		' verify that the level has a beginning and an end.
		If (Character Is Nothing) Then Throw New NotSupportedException("A level must have a starting point.")
		If (_exit = InvalidPosition) Then Throw New NotSupportedException("A level must have an exit.")

	End Sub

	''' <summary>
	''' Loads an individual tile's appearance and behavior.
	''' </summary>
	''' <param name="tileType">
	''' The character loaded from the structure file which
	''' indicates what should be loaded.
	''' </param>
	''' <param name="x">
	''' The X location of this tile in tile space.
	''' </param>
	''' <param name="y">
	''' The Y location of this tile in tile space.
	''' </param>
	''' <returns>The loaded tile.</returns>
	Private Overloads Function LoadTile(tileType As Char, x As Integer, y As Integer) As Tile
		Select Case tileType
			' Blank space
			Case "."c
				Return New Tile(Nothing, TileCollision.Passable)

				' Exit
			Case "X"c
				Return LoadExitTile(x, y)
				' singleing platform
			Case "-"c
				Return LoadTile("Platform", TileCollision.Platform)

				' Various char
				'case 'A':
				'    return LoadEnemyTile(x, y, "MonsterA")
				'case 'B':
				'    return LoadEnemyTile(x, y, "MonsterB")
				'case 'C':
				'    return LoadEnemyTile(x, y, "MonsterC")
				'case 'D':
				'    return LoadEnemyTile(x, y, "MonsterD")

				'platform block
			Case "~"c
				Return LoadVarietyTile("BlockB", 2, TileCollision.Platform)

				'passable block
			Case ":"c
				Return LoadVarietyTile("BlockB", 2, TileCollision.Passable)

				'audrianna start point
			Case "A"c
				Return LoadStartTile(x, y)

				'impassable block
			Case "#"c
				Return LoadVarietyTile("BlockA", 7, TileCollision.Impassable)

				'unknown tile type character
			Case Else
				Throw New NotSupportedException(String.Format("Unsupported tile type character '{0}' at position {1}, {2}.", tileType, x, y))

		End Select
	End Function
	''' <summary>
	''' Remembers the location of the level's exit.
	''' </summary>
	Private Function LoadExitTile(x As Integer, y As Integer) As Tile
		If (_exit <> InvalidPosition) Then Throw New NotSupportedException("A level may only have one exit.")

		_exit = GetBounds(x, y).Center

		Return LoadTile("Exit", TileCollision.Passable)
	End Function

	''' <summary>
	''' Creates a new tile. The other tile loading methods typically chain to this
	''' method after performing their special logic.
	''' </summary>
	''' <param name="name">
	''' Path to a tile texture relative to the Content/Tiles directory.
	''' </param>
	''' <param name="collision">
	''' The tile collision type for the new tile.
	''' </param>
	''' <returns>The new tile.</returns>
	Private Overloads Function LoadTile(name As String, collision As TileCollision) As Tile
		Return New Tile(Content.Load(Of Texture2D)("Tiles/" + name), collision)
	End Function

	''' <summary>
	''' Loads a tile with a random appearance.
	''' </summary>
	''' <param name="baseName">
	''' The content name prefix for this group of tile variations. Tile groups are
	''' name LikeThis0.png and LikeThis1.png and LikeThis2.png.
	''' </param>
	''' <param name="variationCount">
	''' The number of variations in this group.
	''' </param>
	Private Function LoadVarietyTile(baseName As String, variationCount As Integer, collision As TileCollision) As Tile
		Dim index As Integer = _random.Next(variationCount)
		Return LoadTile(baseName & index, collision)
	End Function

	''' <summary>
	''' Instantiates a player, puts him in the level, and remembers where to put him when he is resurrected.
	''' </summary>
	Private Function LoadStartTile(x As Integer, y As Integer) As Tile
		If (_character Is Nothing) Then Throw New NotSupportedException("A level may only have one exit.")

		_exit = GetBounds(x, y).Center
		'gems.Add(new Gem(this, new Vector2(position.X, position.Y)))

		Return New Tile(Nothing, TileCollision.Passable)
	End Function

#End Region

#Region "Bounds and Collision"

	''' <summary>
	''' Gets the collision mode of the tile at a particular location.
	''' This method handles tiles outside of the levels boundries by making it
	''' impossible to escape past the left or right edges, but allowing things
	''' to jump beyond the top of the level and fall off the bottom.
	''' </summary>
	Public Function GetCollision(x As Integer, y As Integer) As TileCollision
		' Prevent escaping past the level ends.
		If (x < 0 Or x >= Width) Then Return TileCollision.Impassable
		' allow jumping past the level top and falling trough the bottom.
		If (y < 0 Or y >= Height) Then Return TileCollision.Passable

		Return _tiles(x, y).Collision
	End Function

	''' <summary>
	''' Gets the bounding rectangle of a tile in world space.
	''' </summary>
	Public Function GetBounds(x%, y%) As Rectangle
		Return New Rectangle(x * Tile.Width, y * Tile.Height, Tile.Width, Tile.Height)
	End Function

	''' <summary>
	''' Width of level measured in tiles.
	''' </summary>
	Public ReadOnly Property Width%
		Get
			Return _tiles.GetLength(0)
		End Get
	End Property

	''' <summary>
	''' Height of level measured in tiles.
	''' </summary>
	Public ReadOnly Property Height%
		Get
			Return _tiles.GetLength(1)
		End Get
	End Property

#End Region

#Region "Update"

	''' <summary>
	''' Updates all objects in the world, performs collision between them,
	''' and handles the time limit with scoring.
	''' </summary>
	Public Sub Update(
	 GameTime As GameTime,
	 KeyboardState As KeyboardState,
	 GamePadState As GamePadState,
	 touchState As TouchCollection,
	 orientation As DisplayOrientation)
		' Pause while the player is dead or time is expired.
		'for (int i = 0 i <= 3 i++) {
		'    if (!Character[i].IsAlive) {
		'        ' Still want to perfrom physics on the player
		'        Character[i].ApplyPhysics(gameTime)
		'    }
		'}
		For Each ch As Hope.Character In Character
			If (Not ch.IsAlive) Then ch.ApplyPhysics(GameTime)
		Next

	End Sub
#End Region
#Region "IDisposable Support"
	Private disposedValue As Boolean ' To detect redundant calls

	' IDisposable
	Protected Overridable Sub Dispose(disposing As Boolean)
		If Not Me.disposedValue Then
			If disposing Then
				' TODO: dispose managed state (managed objects).
				Content.Unload()
			End If

			' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
			' TODO: set large fields to null.
		End If
		Me.disposedValue = True
	End Sub

	' TODO: override Finalize() only if Dispose(ByVal disposing As Boolean) above has code to free unmanaged resources.
	'Protected Overrides Sub Finalize()
	'    ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
	'    Dispose(False)
	'    MyBase.Finalize()
	'End Sub

	' This code added by Visual Basic to correctly implement the disposable pattern.
	Public Sub Dispose() Implements IDisposable.Dispose
		' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
		Dispose(True)
		GC.SuppressFinalize(Me)
	End Sub
#End Region

End Class

Public Enum TileCollision
	''' <summary>
	''' A passable tile is one which does not hinder player motion at all.
	''' </summary>
	Passable = 0

	''' <summary>
	''' An impassable tile is one which does not allow the player to move through
	''' it at all. It is completely solid.
	''' </summary>
	Impassable = 1

	''' <summary>
	''' A platform tile is one which behaves like a passable tile except when the
	''' player is above it. A player can jump up through a platform as well as move
	''' past it to the left and right, but can not fall down through the top of it.
	''' </summary>
	Platform = 2
End Enum

'Stores the appearance and collision behavior of a tile.
Structure Tile
	Public Texture As Texture2D
	Public Collision As TileCollision

	Public Const Width% = 50
	Public Const Height% = 50

	Public Shared ReadOnly Size As Vector2 = New Vector2(Width, Height)


	'construct new tile
	Public Sub New(texture As Texture2D, collision As TileCollision)
		Me.Texture = texture
		Me.Collision = collision
	End Sub

End Structure
